<script setup lang="tsx">
import { NButton, NDivider, NDropdown, NForm, NFormItem, NInput, NPopconfirm, NSelect, NTag } from 'naive-ui';
import { h, ref } from 'vue';
import { useTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales';
import type { XxlJobInfo, XxlJobRegistry } from '@/service/api';
import {
  deleteXxlJobInfo,
  fetchXxlJobGroup,
  fetchXxlJobInfoList,
  fetchXxlJobRegistryList,
  nextTriggerTimeXxlJobInfo,
  startXxlJobInfo,
  stopXxlJobInfo,
  triggerXxlJobInfo
} from '@/service/api';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import SvgIcon from '@/components/custom/svg-icon.vue';
import { useRouterPush } from '@/hooks/common/router';
import { useBoolean } from '~/packages/hooks/src';
import JobInfoSearch from './modules/job-info-search.vue';
import JobInfoOperateModel from './modules/job-info-operate-model.vue';

const appStore = useAppStore();
const { hasAuth } = useAuth();
const { routerPushByKey } = useRouterPush();

const { bool: registeredNodeModelVisible, setTrue: openRegisteredNodeModel } = useBoolean();
const registeredNodeValue = ref<XxlJobRegistry[]>([]);

const { bool: nextExecutionTimeModelVisible, setTrue: openNextExecutionTimeModel } = useBoolean();
const nextExecutionTimeValue = ref<string[]>([]);

const {
  columns,
  columnChecks,
  data,
  loading,
  mobilePagination,
  getData,
  getDataByPage,
  searchParams,
  resetSearchParams
} = useTable({
  apiFn: fetchXxlJobInfoList,
  showTotal: true,
  apiParams: {
    current: 1,
    size: 10,
    jobGroup: null,
    triggerStatus: -1,
    jobDesc: null,
    executorHandler: null,
    author: null
  },
  columns: () => [
    {
      type: 'selection',
      align: 'center',
      width: 48
    },
    {
      key: 'id',
      title: $t('page.base.xxl.job.id'),
      align: 'center',
      ellipsis: {
        tooltip: true
      }
    },
    {
      key: 'jobDesc',
      title: $t('page.base.xxl.job.jobDesc'),
      align: 'center',
      ellipsis: {
        tooltip: true
      }
    },
    {
      key: 'jobGroupDesc',
      title: $t('page.base.xxl.job.jobGroup'),
      align: 'center',
      ellipsis: {
        tooltip: true
      }
    },
    {
      key: 'scheduleType',
      title: $t('page.base.xxl.job.scheduleType'),
      align: 'center',
      render: row => {
        return (
          <div>
            {row.scheduleType}: {row.scheduleConf}
          </div>
        );
      }
    },
    {
      key: 'triggerFrequency',
      title: $t('page.base.xxl.job.triggerFrequency'),
      align: 'center',
      ellipsis: {
        tooltip: true
      }
    },
    {
      key: 'executorHandler',
      title: $t('page.base.xxl.job.executorMode'),
      align: 'center',
      render: row => {
        return (
          <div>
            {row.glueType}: {row.executorHandler}
          </div>
        );
      }
    },
    {
      key: 'author',
      title: $t('page.base.xxl.job.author'),
      align: 'center'
    },
    {
      key: 'triggerStatus',
      title: $t('page.base.xxl.job.triggerStatus'),
      align: 'center',
      render: row => {
        if (row.triggerStatus === null) {
          return null;
        }
        if (row.triggerStatus === 0) {
          return <NTag>STOP</NTag>;
        }
        return <NTag type="info">RUNNING</NTag>;
      }
    },
    {
      key: 'operate',
      title: $t('common.operate'),
      align: 'center',
      fixed: 'right',
      width: 280,
      render: row => (
        <div class="flex-center gap-8px">
          <div v-show={hasAuth('system:job-info:update')}>
            <NButton type="primary" ghost size="small" onClick={() => edit(row.id)}>
              {{
                default: () => $t('common.edit'),
                icon: () => h(SvgIcon, { icon: 'ep:edit' })
              }}
            </NButton>
          </div>
          <div v-show={hasAuth('system:job-info:delete')}>
            <NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
              {{
                default: () => $t('common.confirmDelete'),
                trigger: () => (
                  <NButton type="error" ghost size="small">
                    {{
                      default: () => $t('common.delete'),
                      icon: () => h(SvgIcon, { icon: 'ep:delete' })
                    }}
                  </NButton>
                )
              }}
            </NPopconfirm>
          </div>
          <div v-show={hasAuth(['system:job-info:start', 'system:job-log:query', 'system:job-log:stop'])}>
            <NDropdown
              trigger="hover"
              options={[
                {
                  label: $t('page.base.xxl.job.executeOnce'),
                  show: hasAuth('system:job-info:start'),
                  key: 'executeOnce',
                  icon: () => h(SvgIcon, { icon: 'ant-design:logout-outlined' }),
                  props: {
                    onClick: () => {
                      triggerJob(row);
                    }
                  }
                },
                {
                  label: $t('page.base.xxl.job.logView'),
                  show: hasAuth('system:job-log:query'),
                  key: 'logView',
                  icon: () => h(SvgIcon, { icon: 'ant-design:eye-outlined' }),
                  props: {
                    onClick: () => {
                      routerPushByKey(`base_xxl_log`, {
                        query: {
                          jobId: row.id.toString(),
                          jobGroup: row.jobGroup.toString()
                        }
                      });
                    }
                  }
                },
                {
                  label: $t('page.base.xxl.job.registeredNode'),
                  key: 'registeredNode',
                  icon: () => h(SvgIcon, { icon: 'ant-design:trademark-outlined' }),
                  props: {
                    onClick: () => {
                      handleRegisteredNode(row);
                    }
                  }
                },
                {
                  label: $t('page.base.xxl.job.nextExecutionTime'),
                  show: row.scheduleType !== 'NONE',
                  key: 'nextExecutionTime',
                  icon: () => h(SvgIcon, { icon: 'ant-design:clock-circle-outlined' }),
                  props: {
                    onClick: () => {
                      handleNextExecutionTime(row);
                    }
                  }
                },
                {
                  type: 'divider',
                  key: 'divider'
                },
                {
                  label: $t('page.base.xxl.job.jobStart'),
                  show: hasAuth('system:job-info:start') && row.triggerStatus === 0,
                  key: 'jobStart',
                  icon: () => h(SvgIcon, { icon: 'ant-design:play-circle-outlined' }),
                  props: {
                    onClick: () => {
                      handleJobStart(row.id);
                    }
                  }
                },
                {
                  label: $t('page.base.xxl.job.jobStop'),
                  show: hasAuth('system:job-info:stop') && row.triggerStatus === 1,
                  key: 'jobStart',
                  icon: () => h(SvgIcon, { icon: 'ant-design:play-circle-outlined' }),
                  props: {
                    onClick: () => {
                      handleJobStop(row.id);
                    }
                  }
                },
                {
                  label: $t('page.base.xxl.job.jobCopy'),
                  key: 'jobCopy',
                  icon: () => h(SvgIcon, { icon: 'ant-design:copy-outlined' }),
                  props: {
                    onClick: () => {
                      handleJobCopy(row.id);
                    }
                  }
                }
              ]}
            >
              {{
                default: () => (
                  <NButton type="error" size="small" ghost>
                    {{
                      default: () => $t('common.more'),
                      icon: () => h(SvgIcon, { icon: 'ep:d-arrow-right' })
                    }}
                  </NButton>
                )
              }}
            </NDropdown>
          </div>
        </div>
      )
    }
  ]
});

const {
  modelVisible,
  handleAdd,
  handleEdit,
  handleCopy,
  onDeleted,
  onBatchDeleted,
  checkedRowKeys,
  editingData,
  operateType
} = useTableOperate(data, getData);

/**
 * delete
 *
 * @param id
 */
async function handleDelete(id: number) {
  const { error } = await deleteXxlJobInfo(id.toString());
  await onDeleted(error);
}
async function handleBatchDelete() {
  const { error } = await deleteXxlJobInfo(checkedRowKeys.value.join(','));
  await onBatchDeleted(error);
}

/**
 * edit
 *
 * @param id
 */
function edit(id: number) {
  handleEdit(id);
}

/**
 * 任务执行一次
 *
 * @param row
 */
const executorParam = ref<string>('');
const addressList = ref<string>('');
async function triggerJob(row: XxlJobInfo) {
  executorParam.value = '';
  addressList.value = '';
  const options: any[] = [];
  const { data: res } = await fetchXxlJobRegistryList({
    id: row.jobGroup
  });
  if (res !== null) {
    res.records.forEach(item => {
      options.push({
        label: item.address,
        value: item.address
      });
    });
  }
  window.$dialog?.info({
    title: $t('page.base.xxl.job.executeOnce'),
    style: 'width: 25%;',
    content: () => (
      <div class="mt-5">
        <NDivider />
        <NForm label-placement="left">
          <NFormItem label={$t('page.base.xxl.job.executorParam')}>
            <NInput
              v-model={executorParam.value}
              type="textarea"
              placeholder={$t('page.base.xxl.job.form.executorParam')}
              onChange={handleExecutorParamChange}
            />
          </NFormItem>
          <NFormItem label={$t('page.base.xxl.job.executorAddressList')}>
            <NSelect
              v-model={addressList.value}
              options={options}
              placeholder={$t('page.base.xxl.job.form.executorAddressList')}
              onUpdate:value={handleAddressListChange}
            />
          </NFormItem>
        </NForm>
        <NDivider />
      </div>
    ),
    positiveText: $t('common.confirm'),
    negativeText: $t('common.cancel'),
    onPositiveClick: () => {
      triggerXxlJobInfo(row.id, executorParam.value, addressList.value);
      window.$message?.success($t('page.base.xxl.job.executeOnceSuccess'));
      getDataByPage();
    },
    onNegativeClick: () => {
      executorParam.value = '';
      addressList.value = '';
    }
  });
}
function handleExecutorParamChange(value: string | [string, string]) {
  executorParam.value = value.toString();
}
function handleAddressListChange(value: string | [string, string]) {
  addressList.value = value.toString();
}

/**
 * 注册节点
 *
 * @param row
 * @param type
 */
async function handleRegisteredNode(row: XxlJobInfo) {
  registeredNodeValue.value = [];
  const { data: res } = await fetchXxlJobGroup(row.jobGroup);
  if (res !== null) {
    registeredNodeValue.value = res.registryList;
  }
  openRegisteredNodeModel();
}

/**
 * 下次执行时间
 *
 * @param row
 */
async function handleNextExecutionTime(row: XxlJobInfo) {
  nextExecutionTimeValue.value = [];
  const { data: res } = await nextTriggerTimeXxlJobInfo(row.scheduleType, row.scheduleConf);
  if (res !== null) {
    nextExecutionTimeValue.value = res;
  }
  openNextExecutionTimeModel();
}

/**
 * 启动任务
 *
 * @param jobId
 */
function handleJobStart(jobId: number) {
  window.$dialog?.warning({
    title: $t('page.base.xxl.job.jobStart'),
    style: 'width: 15%;',
    content: $t('page.base.xxl.job.jobStartAffirm'),
    positiveText: $t('common.confirm'),
    negativeText: $t('common.cancel'),
    onPositiveClick: () => {
      startXxlJobInfo(jobId);
      window.$message?.success($t('page.base.xxl.job.startSuccess'));
      getDataByPage();
    },
    onNegativeClick: () => {}
  });
}

/**
 * 停止任务
 *
 * @param jobId
 */
function handleJobStop(jobId: number) {
  window.$dialog?.warning({
    title: $t('page.base.xxl.job.jobStop'),
    style: 'width: 15%;',
    content: $t('page.base.xxl.job.jobStopAffirm'),
    positiveText: $t('common.confirm'),
    negativeText: $t('common.cancel'),
    onPositiveClick: () => {
      stopXxlJobInfo(jobId);
      window.$message?.success($t('page.base.xxl.job.startSuccess'));
      getDataByPage();
    },
    onNegativeClick: () => {}
  });
}

/**
 * 复制任务
 *
 * @param id
 */
function handleJobCopy(id: number) {
  handleCopy(id);
}
</script>

<template>
  <div class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <JobInfoSearch
      v-if="hasAuth('system:job-info:query')"
      v-model:model="searchParams"
      @reset="resetSearchParams"
      @search="getDataByPage"
    />
    <NCard :title="$t('page.base.route.title')" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
      <template #header-extra>
        <TableHeaderOperation
          v-model:columns="columnChecks"
          :disabled-delete="checkedRowKeys.length === 0"
          :loading="loading"
          create-auth="system:job-info:create"
          delete-auth="system:job-info:delete"
          delete-show="false"
          @add="handleAdd"
          @delete="handleBatchDelete"
          @refresh="getData"
        />
      </template>
      <NDataTable
        v-model:checked-row-keys="checkedRowKeys"
        :columns="columns"
        :data="data"
        size="small"
        :flex-height="!appStore.isMobile"
        :scroll-x="2088"
        :loading="loading"
        :row-key="row => row.id"
        remote
        :pagination="mobilePagination"
        class="sm:h-full"
      />

      <JobInfoOperateModel
        v-model:visible="modelVisible"
        :operate-type="operateType"
        :row-data="editingData"
        @submitted="getDataByPage"
      />
    </NCard>
    <!-- 弹窗 -->
    <NModal
      v-model:show="registeredNodeModelVisible"
      :title="$t('page.base.xxl.job.registeredNode')"
      preset="card"
      class="w-100"
    >
      <NScrollbar class="max-h-150">
        <div v-for="item in registeredNodeValue" :key="item.address">
          <NTag v-if="item.status === 1" type="success" class="mb-2">{{ item.address }}</NTag>
          <NTag v-if="item.status === 0" type="error" class="mb-2">{{ item.address }}</NTag>
        </div>
      </NScrollbar>
    </NModal>
    <NModal
      v-model:show="nextExecutionTimeModelVisible"
      :title="$t('page.base.xxl.job.nextExecutionTime')"
      preset="card"
      class="w-100"
    >
      <NScrollbar class="max-h-150">
        <div v-for="item in nextExecutionTimeValue" :key="item">
          <NTag class="mb-2">{{ item }}</NTag>
        </div>
      </NScrollbar>
    </NModal>
  </div>
</template>

<style scoped></style>
